/**
 * Copyright © 2020 易久批信息技术有限公司. All rights reserved.
 */
package com.dysm.imageprint;

import org.springframework.boot.ansi.*;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 *
 * @author xiay
 * @since 2020/12/3 9:54 上午
 */
public class Print {

    private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };

    private static final char[] PIXEL = { ' ', '.', '*', ':', 'o', '&', '8', '#', '@'};

//    private static final int[] COLOR = { 30, 31, 33, 32, 36, 35, 34, 38};
//    private static final int[] COLOR = { 97, 91, 33, 93, 92, 96, 94, 95, 30};
    private static final int[] COLOR = { 30, 95, 94, 96, 92, 93, 33, 91, 97};

    private static final int LUMINANCE_INCREMENT = 10;

    private static final int LUMINANCE_START = LUMINANCE_INCREMENT * PIXEL.length;

    private static final int COLOR_INCREMENT = 10;

    private static final int COLOR_START = COLOR_INCREMENT * COLOR.length;

    /**
     * @param image
     * @param margin
     * @param invert
     * @param out
     */
    public static void printImage(BufferedImage image, int margin, boolean invert,
                                    PrintStream out) {
        AnsiElement background = (invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT);
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(background));
        out.println();
        out.println();
        AnsiColor lastColor = AnsiColor.DEFAULT;
        for (int y = 0; y < image.getHeight(); y++) {
            for (int i = 0; i < margin; i++) {
                out.print(" ");
            }
            for (int x = 0; x < image.getWidth(); x++) {
                Color color = new Color(image.getRGB(x, y), false);
                AnsiColor ansiColor = AnsiColors.getClosest(color);
                if (ansiColor != lastColor) {
                    out.print(AnsiOutput.encode(ansiColor));
                    lastColor = ansiColor;
                }
                char asciiPixel = getAsciiPixel(color, invert);
                int c = getColorNew(color);
                String formatLogString = ConsoleColorPrintTest.getFormatLogString(String.valueOf(asciiPixel), c, 0);
                out.print(formatLogString);
            }
            out.println();
        }
        out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
        out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
        out.println();
    }

    private static char getAsciiPixel(Color color, boolean dark) {
        double luminance = getLuminance(color, dark);
        for (int i = 0; i < PIXEL.length; i++) {
            if (luminance >= (LUMINANCE_START - (i * LUMINANCE_INCREMENT))) {
                return PIXEL[i];
            }
        }
        return PIXEL[PIXEL.length - 1];
    }

    private static int getColor(Color color, boolean dark) {
        double luminance = getLuminance(color, dark);
        for (int i = 0; i < COLOR.length; i++) {
            if (luminance >= (COLOR_START - (i * COLOR_INCREMENT))) {
                return COLOR[i];
            }
        }
        return COLOR[COLOR.length - 1];
    }

    private static int getColorNew(Color color) {
        int red = color.getRed();
        if (red == 0) {
            return 30;
        }
        if (red == 255) {
            return 97;
        }
        int green = color.getGreen();
        int blue = color.getBlue();
        Map<Integer, String> map = new HashMap<>();
        map.put(red, "red");
        map.put(green, "green");
        map.put(blue, "blue");
        int max = Math.max(red, green);
        max = Math.max(max, blue);
        String s = map.get(max);
        if (Objects.equals(s, "red")) {
            return 91;
        } else if (Objects.equals(s, "green")) {
            return 92;
        }
        return 94;
    }

    private static int getLuminance(Color color, boolean inverse) {
        double luminance = 0.0;
        luminance += getLuminance(color.getRed(), inverse, RGB_WEIGHT[0]);
        luminance += getLuminance(color.getGreen(), inverse, RGB_WEIGHT[1]);
        luminance += getLuminance(color.getBlue(), inverse, RGB_WEIGHT[2]);
        return (int) Math.ceil((luminance / 0xFF) * 100);
    }

    private static double getLuminance(int component, boolean inverse, double weight) {
        return (inverse ? 0xFF - component : component) * weight;
    }
}
